package com.thoughtworks.impersonator.http.message.storage;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;

import com.thoughtworks.impersonator.http.message.HttpExchange;

public class HttpExchangeStorage {

    private static final String UNKNOWN_FILES_DIR_NAME = "unknownFiles";
    private static final String ENCODING = "UTF-8";
    private File storageDir;
    private HttpExchangeSerialiser serialiser;
    private static Logger log = Logger.getLogger(HttpExchangeStorage.class);
    private static final FilenameFilter REQUEST_FILES = new FilenameFilter() {
        public boolean accept(File dir, String name) {
            return name.toLowerCase().endsWith(".request");
        }
    };

    public HttpExchangeStorage(File storageDir, HttpExchangeSerialiser serialiser) {
        ensureDirectoryExists(storageDir);
        this.storageDir = storageDir;
        this.serialiser = serialiser;
    }

    public void store(HttpExchange exchange) {
        String filename = storageDir.getAbsolutePath() + "/" + serialiser.identify(exchange.request());
        save(serialiser.serialise(exchange.request()), new File(filename + ".request"));
        save(serialiser.serialise(exchange.response()), new File(filename + ".response"));
    }

    public HttpResponse loadResponseFor(HttpRequest request) {
        String uid = serialiser.identify(request);
        File recorded = new File(storageDir + "/" + uid + ".response");
        if (recorded.exists()) {
            log.debug("recorded message found with id: " + uid);
            return serialiser.deserialiseResponse(contents(recorded));
        } else {
            File unknownFilesDir = getUnkwownFilesDirectory();
            File unknownFile = new File(unknownFilesDir, uid + ".notfound");
            log.warn("unknown request, saving contents to: " + unknownFile.getAbsolutePath());
            save(serialiser.serialise(request), unknownFile);
            return notFound();
        }
    }

    public List<HttpExchange> all() {
        List<HttpExchange> results = new ArrayList<HttpExchange>();
        for (File requestFile : storageDir.listFiles(REQUEST_FILES)) {
            File responseFile = new File(requestFile.getAbsolutePath().replaceAll("request$", "response"));
            results.add(new HttpExchange(serialiser.deserialiseRequest(contents(requestFile)), serialiser.deserialiseResponse(contents(responseFile))));
        }
        return results;
    }

    private HttpResponse notFound() {
        HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND);
        response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8");
        return response;
    }

    private String contents(File recorded) {
        try {
            return FileUtils.readFileToString(recorded, ENCODING);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void ensureDirectoryExists(File directory) {
        if (!directory.exists()) {
            if (!directory.mkdirs()) {
                log.debug(String.format("failed to create storage directory at '%s'", directory.getAbsolutePath()));
            }
        }
    }

    private void save(String contents, File unknownFile) {
        try {
            FileUtils.writeStringToFile(unknownFile, contents, ENCODING);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public File getUnkwownFilesDirectory() {
        return new File(storageDir, UNKNOWN_FILES_DIR_NAME);
    }

}
